#include <Arduino.h>

/*Using LVGL with Arduino requires some extra steps:
 *Be sure to read the docs here: https://docs.lvgl.io/master/get-started/platforms/arduino.html  */

// #include <lvgl.h>

/*To use the built-in examples and demos of LVGL uncomment the includes below respectively.
 *You also need to copy `lvgl/examples` to `lvgl/src/examples`. Similarly for the demos `lvgl/demos` to `lvgl/src/demos`.
 Note that the `lv_examples` library is for LVGL v7 and you shouldn't install it for this version (since LVGL v8)
 as the examples and demos are now part of the main LVGL library. */

// #include <examples/lv_examples.h>
#include "lvgl/lvgl.h"
#include "lvgl/examples/lv_examples.h"
#include "lvgl/demos/lv_demos.h"
// #include "swap_bytes.h"
#define DIRECT_MODE // Uncomment to enable full frame buffer
#include <Arduino_GFX_Library.h>
#include "clove2gfx.h"
#include "TP_FT3168.h"
#include "clove_encoder/Clove_Encoder.h"
// #include "touch.h"
#include <app.h>
#include <clove_pin_config.h>

#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>
#ifndef PSTR
#define PSTR // Make Arduino Due happy
#endif

#define PIN 14
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(14, 5, 1, 1, PIN,
                                               NEO_TILE_BOTTOM + NEO_TILE_RIGHT + NEO_TILE_ROWS + NEO_TILE_ZIGZAG +
                                                   NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_PROGRESSIVE,
                                               NEO_GRB + NEO_KHZ800);

Clove_Encoder encoderSensor;

uint32_t screenWidth;
uint32_t screenHeight;
uint32_t bufSize;
lv_disp_draw_buf_t draw_buf;
lv_color_t *disp_draw_buf;
lv_disp_drv_t disp_drv;
TP_FT3168 tp;

lv_group_t *g;
// Adafruit_ST7

/*---------music-spectrum--------*/

#include <driver/i2s.h>
#include "I2SSampler.h"
#include "Processor.h"

#define CENTER_Y (EXAMPLE_LCD_V_RES / 2)
#define WINDOW_HEIGHT (EXAMPLE_LCD_V_RES - 8)
// I2S
#define WINDOW_SIZE 512
#define SAMPLE_SIZE 32
#define I2S_BCK_PIN -1
// #define I2S_WS_PIN   42
// #define I2S_DATA_PIN 41

// #define PIN_CLK 8
// #define PIN_DATA 0

// #define MODE_MIC 0
// #define MIC_I2S_CHANNEL I2S_NUM_0

#define I2S_WS_PIN 8
#define I2S_DATA_PIN 1 // static void i2s_read_task(void *param);
static void audio_processing_task(void *param);
static void bar_value_update(float *mag);
static void spectrum_draw_event_cb(lv_event_t *e);

static int x_step = int(EXAMPLE_LCD_H_RES / SAMPLE_SIZE);
lv_obj_t *spectrum_obj = NULL;

I2SSampler i2s_sampler;
Processor audio_processor(WINDOW_SIZE);
TaskHandle_t processing_task_handle;
float bar_chart_peaks[WINDOW_SIZE] = {0};
float bar_chart[WINDOW_SIZE] = {0};

uint32_t color_map[] = {
    0x24c4e6, 0x05a6fb, 0x0571fb, 0x053ffb, 0x0509fb, 0x3305fb, 0x6905fb,
    0x9705fb, 0xcd05fb, 0xfb05f7, 0xfb05c1, 0xfb058f, 0xfb055a, 0xfb0528,
    0xfb1505, 0xfb4a05, 0xfb7c05, 0xfbb205, 0xfbe405, 0xe0fb05, 0xaefb05,
    0x78fb05, 0x46fb05, 0x11fb05, 0x05fb2c, 0x05fb5d, 0x05fb93, 0x05fbc5,
    0x05fbfb, 0x05c9fb, 0x0593fb, 0x0584fb};

uint32_t new_color_map2[] = {
    0xff3366, 0xff9900, 0xffff33, 0x99ff66, 0x66ff00, 0x33cc33, 0x00ff99,
    0x3366ff, 0x6699ff, 0x0000cc, 0x993399, 0xcc00cc, 0xcc66ff, 0xee33ee,
    0xcc3333, 0xff3399, 0xff99cc, 0xffccff, 0xffd766, 0xff6600, 0xff0000,
    0xcb8989, 0xff99ee, 0xcc66cc, 0xff00ff, 0xee99ee, 0xcc9966, 0xff9933,
    0xff6633, 0xffcc00, 0xffff66, 0xe0fb05};

uint32_t new_color_map[] = {
    0xff6347, 0xff8c00, 0xffff00, 0xadff2f, 0x7fff00, 0x32cd32, 0x00fa9a,
    0x00ced1, 0x4682b4, 0x1e90ff, 0x0000ff, 0x8a2be2, 0x9400d3, 0x9932cc,
    0xba55d3, 0xdc143c, 0xff1493, 0xff69b4, 0xffb6c1, 0xffd700, 0xff4500,
    0xff0000, 0xdb7093, 0xee82ee, 0xda70d6, 0xff00ff, 0xd8bfd8, 0xd2691e,
    0xff7f50, 0xff4500, 0xff8c00, 0xffff00};

uint32_t cold_color_map[] = {
    0x0000ff, 0x0011ff, 0x0022ff, 0x0033ff, 0x0044ff, 0x0055ff, 0x0066ff,
    0x0077ff, 0x0088ff, 0x0099ff, 0x00aaff, 0x00bbff, 0x00ccff, 0x00ddff,
    0x00eeff, 0x00ffff, 0x00ffe0, 0x00ffc0, 0x00ffa0, 0x00ff80, 0x00ff60,
    0x00ff40, 0x00ff20, 0x00ff00, 0x20ff00, 0x40ff00, 0x60ff00, 0x80ff00,
    0xa0ff00, 0xc0ff00, 0xe0ff00, 0xffff00};

uint32_t warm_color_map[] = {
    0xff0000, 0xff1100, 0xff2200, 0xff3300, 0xff4400, 0xff5500, 0xff6600,
    0xff7700, 0xff8800, 0xff9900, 0xffaa00, 0xffbb00, 0xffcc00, 0xffdd00,
    0xffee00, 0xffff00, 0xffe000, 0xffc000, 0xffa000, 0xff8000, 0xff6000,
    0xff4000, 0xff2000, 0xff0000, 0xff0020, 0xff0040, 0xff0060, 0xff0080,
    0xff00a0, 0xff00c0, 0xff00e0, 0xff00ff};

uint32_t gradient_color_map[] = {
    0x0000ff, 0x0022ff, 0x0044ff, 0x0066ff, 0x0088ff, 0x00aaff, 0x00ccff,
    0x00eeff, 0x00ffff, 0x00ffdd, 0x00ffbb, 0x00ff99, 0x00ff77, 0x00ff55,
    0x00ff33, 0x00ff11, 0x00ff00, 0x22ff00, 0x44ff00, 0x66ff00, 0x88ff00,
    0xaaff00, 0xccff00, 0xeeff00, 0xffff00, 0xffdd00, 0xffbb00, 0xff9900,
    0xff7700, 0xff5500, 0xff3300, 0xff1100};

uint32_t color_mapx[] = 
{
    0xE2F5FB,
    0xEBE6C9,
    0xE3C445,
    0xEBE81D,
    0xF99437,
    0xE0886C,
    0xFE5740,
    0xD13B10,
    0xFC41EB,
    0xF81ECD,
    0xE119AD,
    0xAC5896,
    0x6257FD,
    0x6358BA,
    0x522FB5,
    0x4B119F,
    0x36C1FC,
    0x1A81B4,
    0x138F9B,
    0x52E9DC,
    0x07CFD8,
    0x69D88D,
    0x4BED75,
    0x3FF7B0,
    0x889B77,
    0x187520,
    0x238B1B,
    0x0A8A0A,
    0x564D12,
    0x6C3C44,
    0x8E9FAD,
    0x7DE4FF
};

uint32_t *color_series[] = {
    cold_color_map,
    warm_color_map,
    gradient_color_map,
    color_map,
    color_mapx};

// i2s config for reading mic
i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM),
    .sample_rate = 64000,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
    .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
    .communication_format = I2S_COMM_FORMAT_STAND_I2S,
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 3,
    .dma_buf_len = 1024,
};

// i2s pins
i2s_pin_config_t i2s_pins = {.bck_io_num = I2S_PIN_NO_CHANGE,
                             .ws_io_num = I2S_WS_PIN,
                             .data_out_num = I2S_PIN_NO_CHANGE,
                             .data_in_num = I2S_DATA_PIN};

/* Creates a semaphore to handle concurrent call to lvgl stuff
 * If you wish to call *any* lvgl function from other threads/tasks
 * you should lock on the very same semaphore! */
SemaphoreHandle_t xGuiSemaphore;

#if LV_USE_LOG != 0
/* Serial debugging */
void my_print(const char *buf)
{
  Serial.printf(buf);
  Serial.flush();
}
#endif

/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
#ifndef DIRECT_MODE
  uint32_t w = (area->x2 - area->x1 + 1);
  uint32_t h = (area->y2 - area->y1 + 1);

#if (LV_COLOR_16_SWAP != 0)
  gfx->draw16bitBeRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h);
#else
  gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h);
#endif
#endif // #ifndef DIRECT_MODE

  lv_disp_flush_ready(disp_drv);
}

/*Read the touchpad*/

static void _touchpad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
{
  TouchPoint_t tpp;

  tp.getTouchRaw(tpp);
  data->state = LV_INDEV_STATE_REL;
  if (tpp.x != -1)
  {
    // printf("%d %d\n", tpp.x, tpp.y);
    data->point.x = tpp.x;
    data->point.y = tpp.y;
    data->state = LV_INDEV_STATE_PR;
  }
}

static void lv_encoder_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
{
  static int encoderValue = 0;
  data->state = LV_INDEV_STATE_RELEASED;
  if (encoderSensor.getButtonStatus())
  {
    data->state = LV_INDEV_STATE_PR;
    // ENTER_BUTTON = false;
  }
  if (encoderSensor.getEncoderValue() < encoderValue)
  {
    data->enc_diff = -1;
  }
  else if (encoderSensor.getEncoderValue() > encoderValue)
  {
    data->enc_diff = 1;
  }
  encoderValue = encoderSensor.getEncoderValue();
}

static void matrixRain(void *param)
{
  const int width = 5;
  const int height = 14;
  int size = width * height;
#define fieldsize width *(height + 1)
  int field[fieldsize] = {0};

  matrix.begin();
  // matrix.setRotation(1);
  // matrix.setTextWrap(false);
  matrix.setBrightness(50);
  matrix.setRotation(3);
  while (true)
  {

    for (int i = 0; i < width; i++)
      field[i] = 0;
    if (random(2))
    {
      int index = random(width) % width;
      Serial.println(index);
      field[index] = 15; // brightness
    }
    for (int i = fieldsize - 1; i > width - 1; i--)
    {
      if (field[i - width] == 15)
        field[i] = 15; // 上层是15的亮度，下一层继承上一层的亮度。
      if (field[i - width] > 0)
        field[i - width] -= 5; // 亮度都在减弱。
    }
    // matrix.fillScreen(0);
    uint16_t colorPixel;
    for (int i = 0; i < size; i++)
    {
      if (field[i + width] == 0)
        colorPixel = matrix.Color(0, 0, 0);
      // else if (field[i + width] == 15)
      //   colorPixel = matrix.Color(0, 255, 0);
      else
        colorPixel = matrix.Color(0, field[i], field[i] * 50);

      matrix.drawPixel(i % width, i / width, colorPixel); // 位点的行和列所在的像素位置。 x,y
      // lcd.drawString(String(string[random(len)]), i % width*16, i / width*16);
      // lcd.print(String(str[random(len)]));
    }
    // matrix.drawPixel(5,1,matrix.Color(255,255,255));
    matrix.show();
    delay(30);
  }
}




void setup()
{
  Serial.begin(115200);
  // Serial.setDebugOutput(true);
  // while(!Serial);
  Serial.println("Arduino_GFX LVGL_Arduino_v8 example ");
  String LVGL_Arduino = String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();
  Serial.println(LVGL_Arduino);

  setup_pmu();
  // Init Display

  setup_pcf8574();
  pcf.digitalWrite(PCF8574_TP_RST, HIGH);
  pcf.digitalWrite(PCF8574_TP_RST, LOW);
  pcf.digitalWrite(PCF8574_TP_RST, HIGH);

  pcf.digitalWrite(PCF8574_LCD_RST, HIGH);
  pcf.digitalWrite(PCF8574_LCD_RST, LOW);
  pcf.digitalWrite(PCF8574_LCD_RST, HIGH);

  encoderSensor.begin();

#ifdef GFX_EXTRA_PRE_INIT
  GFX_EXTRA_PRE_INIT();
#endif

  // Init Display
  if (!gfx->begin())
  {
    Serial.println("gfx->begin() failed!");
  }
  // gfx->invertDisplay(1);
  // gfx->setRotation(1);
  gfx->fillScreen(BLACK);

#ifdef GFX_BL
  ledcSetup(1, 12000, 8);   // 12 kHz PWM, 8-bit resolution
  ledcAttachPin(GFX_BL, 1); // assign RGB led pins to channels
  ledcWrite(1, 63);
#endif

  // Init touch device
  // touch_init(gfx->width(), gfx->height(), gfx->getRotation());

  lv_init();
  tp.init(2, 3, -1, 10, true, 400000);

  static lv_indev_drv_t indev_drv;
#if LV_USE_LOG != 0
  lv_log_register_print_cb(my_print); /* register print function for debugging */
#endif

  screenWidth = gfx->width();
  screenHeight = gfx->height();

#ifdef DIRECT_MODE
  bufSize = screenWidth * screenHeight;
#else
  bufSize = screenWidth * 40;
#endif

#ifdef ESP32
#if defined(DIRECT_MODE) && (defined(CANVAS) || defined(RGB_PANEL))
  disp_draw_buf = (lv_color_t *)gfx->getFramebuffer();
#else  // !(defined(DIRECT_MODE) && (defined(CANVAS) || defined(RGB_PANEL)))
  disp_draw_buf = (lv_color_t *)heap_caps_malloc(bufSize * 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
  if (!disp_draw_buf)
  {
    // remove MALLOC_CAP_INTERNAL flag try again
    disp_draw_buf = (lv_color_t *)heap_caps_malloc(bufSize * 2, MALLOC_CAP_8BIT);
  }
#endif // !(defined(DIRECT_MODE) && (defined(CANVAS) || defined(RGB_PANEL)))
#else  // !ESP32
  Serial.println("LVGL disp_draw_buf heap_caps_malloc failed! malloc again...");
  disp_draw_buf = (lv_color_t *)malloc(bufSize * 2);
#endif // !ESP32
  if (!disp_draw_buf)
  {
    Serial.println("LVGL disp_draw_buf allocate failed!");
  }
  else
  {
    lv_disp_draw_buf_init(&draw_buf, disp_draw_buf, NULL, bufSize);

    /* Initialize the display */
    lv_disp_drv_init(&disp_drv);
    /* Change the following line to your display resolution */
    disp_drv.hor_res = screenWidth;
    disp_drv.ver_res = screenHeight;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.draw_buf = &draw_buf;
#ifdef DIRECT_MODE
    disp_drv.direct_mode = true;
#endif
    lv_disp_drv_register(&disp_drv);

    /* Initialize the (dummy) input device driver */
    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    // indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.type = LV_INDEV_TYPE_ENCODER;
    // indev_drv.read_cb = _touchpad_read;
    indev_drv.read_cb = lv_encoder_read;
    lv_indev_drv_register(&indev_drv);
    static lv_indev_t *lv_encoder_indev = lv_indev_drv_register(&indev_drv);
    g = lv_group_create();
    lv_indev_set_group(lv_encoder_indev, g);
    lv_group_set_default(g);

    /* Option 3: Or try out a demo. Don't forget to enable the demos in lv_conf.h. E.g. LV_USE_DEMOS_WIDGETS*/
    // lv_demo_widgets();
    // lv_demo_benchmark();
    // lv_demo_keypad_encoder();
    // lv_demo_music();
    // lv_demo_stress();
    // lv_example_dropdown_1();

    xGuiSemaphore = xSemaphoreCreateMutex();
    xSemaphoreGive(xGuiSemaphore);
    xTaskCreatePinnedToCore(matrixRain, "matrixRain",
                            4096, NULL, 2, NULL, 0);
#if 1
    spectrum_obj = lv_obj_create(lv_scr_act());
    lv_obj_remove_style_all(spectrum_obj);
    // lv_obj_refresh_ext_draw_size(spectrum_obj);
    lv_obj_set_size(spectrum_obj, EXAMPLE_LCD_H_RES, EXAMPLE_LCD_V_RES);
    // lv_obj_set_pos(spectrum_obj, 1, 1);
    lv_obj_center(spectrum_obj);
    lv_obj_clear_flag(spectrum_obj,
                      LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_SCROLLABLE);
    lv_obj_set_style_bg_color(spectrum_obj, lv_color_black(), 0);

    lv_obj_set_style_bg_opa(spectrum_obj, LV_OPA_100, 0);
    lv_obj_add_event_cb(spectrum_obj, spectrum_draw_event_cb, LV_EVENT_DRAW_POST,
                        NULL);

    xTaskCreatePinnedToCore(audio_processing_task, "audio processing task",
                            4096, NULL, 2, &processing_task_handle, 0);
    i2s_sampler.start(
        I2S_NUM_0, i2s_pins, i2s_config, WINDOW_SIZE,
        processing_task_handle); // Put the handle inside the sampler

    Serial.println("Setup done");
#endif
  }
  // setup_tca8418();
}
void loop()
{
  // lv_timer_handler(); /* let the GUI do its work */

  // loop_tca8418();

  vTaskDelay(pdMS_TO_TICKS(10));
  /* Try to take the semaphore, call lvgl related function on success */
  if (pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY))
  {
    lv_task_handler();
    xSemaphoreGive(xGuiSemaphore);
  }
  // delay(5);
#if 1
#ifdef DIRECT_MODE
#if defined(CANVAS) || defined(RGB_PANEL)
  gfx->flush();
#else // !(defined(CANVAS) || defined(RGB_PANEL))
#if (LV_COLOR_16_SWAP != 0)
  // gfx->draw16bitRGBBitmap(0, 0, (uint16_t *)disp_draw_buf, screenWidth, screenHeight);

  gfx->draw16bitBeRGBBitmap(0, 0, (uint16_t *)disp_draw_buf, screenWidth, screenHeight);
  // swapBytes((uint8_t *)gfx->getFramebuffer(), disp_draw_buf, screenWidth *screenHeight * 2);
#else
  gfx->draw16bitRGBBitmap(0, 0, (uint16_t *)disp_draw_buf, screenWidth, screenHeight);
  // gfx->draw16bitBeRGBBitmap(0, 0, (uint16_t *)disp_draw_buf, screenWidth, screenHeight);

#endif
#endif // !(defined(CANVAS) || defined(RGB_PANEL))
#else  // !DIRECT_MODE
#ifdef CANVAS
  gfx->flush();
#endif
#endif // !DIRECT_MODE
#endif
}

static void audio_processing_task(void *param)
{
  const TickType_t xMaxBlockTime = pdMS_TO_TICKS(100);
  while (true)
  {
    // wait for some samples to process
    uint32_t ulNotificationValue = ulTaskNotifyTake(pdTRUE, xMaxBlockTime);
    if (ulNotificationValue > 0)
    {
      int16_t *samples = i2s_sampler.getCapturedAudioBuffer();
      audio_processor.update(samples);
      bar_value_update(audio_processor.m_energy);
      lv_obj_invalidate(spectrum_obj);
    }
  }
}

static void bar_value_update(float *mag)
{
  for (int i = 2, k = 0; k < SAMPLE_SIZE; i += 2, k++)
  {
    //  int ave = 0;
    float ave = 0;
    for (int j = 0; j < 2; j++)
    {
      ave += mag[i + j];
    }
    ave /= 12; // 4 is for average ,12 for down scale.
    int bar_value = std::min(float(WINDOW_HEIGHT), ave);
    bar_chart[k] = (bar_value > bar_chart[k])
                       ? bar_value
                       : 0.7 * bar_chart[k] + 0.3 * bar_value;
    bar_chart_peaks[k] = (bar_value > bar_chart_peaks[k])
                             ? bar_value
                             : 0.9 * bar_chart_peaks[k] + 0.1 * bar_value;
  }
}

static void spectrum_draw_event_cb(lv_event_t *e)
{
  lv_event_code_t code = lv_event_get_code(e);

  if (code == LV_EVENT_DRAW_POST)
  {
    lv_obj_t *obj = lv_event_get_target(e);
    lv_draw_ctx_t *draw_ctx = lv_event_get_draw_ctx(e);
    lv_draw_rect_dsc_t draw_rect_dsc;
    lv_draw_rect_dsc_init(&draw_rect_dsc);
    draw_rect_dsc.bg_opa = LV_OPA_100;
    draw_rect_dsc.bg_color = lv_color_white();
    lv_draw_line_dsc_t draw_line_dsc;
    lv_draw_line_dsc_init(&draw_line_dsc);
    draw_line_dsc.width = 2;
    draw_line_dsc.color = lv_color_white();

    for (int i = 0; i < SAMPLE_SIZE; i++)
    {
      lv_area_t _rect;
      draw_rect_dsc.bg_color = lv_color_hex(color_series[2][i]);
      _rect.x1 = i * x_step;
      _rect.x2 = i * x_step + 3;
      _rect.y1 = CENTER_Y - int(bar_chart[i] / 2);
      _rect.y2 = CENTER_Y + int(bar_chart[i] / 2);
      lv_draw_rect(draw_ctx, &draw_rect_dsc, &_rect);

      draw_line_dsc.color = lv_color_hex(color_series[2][i]);

      lv_point_t above_line[2];
      /* upside line always 2 px above the bar */
      above_line[0].x = i * x_step;
      above_line[0].y = CENTER_Y - int(bar_chart_peaks[i] / 2) - 2;
      above_line[1].x = i * x_step + 3;
      above_line[1].y = CENTER_Y - int(bar_chart_peaks[i] / 2) - 2;
      lv_draw_line(draw_ctx, &draw_line_dsc, &above_line[0],
                   &above_line[1]);

      lv_point_t blow_line[2];
      /* under line always 2 px below the bar */

      blow_line[0].x = i * x_step;
      blow_line[0].y = CENTER_Y + int(bar_chart_peaks[i] / 2) + 2;
      blow_line[1].x = i * x_step + 3;
      blow_line[1].y = CENTER_Y + int(bar_chart_peaks[i] / 2) + 2;
      lv_draw_line(draw_ctx, &draw_line_dsc, &blow_line[0],
                   &blow_line[1]);
    }
  }
}
